//
// Created by liuml on 2018/10/19.
//


#include "XYAudio.h"

XYAudio::XYAudio(XYPlayStatus *playStatus, int sample_rate, XYCallJava *callJava) {

    this->playStatus = playStatus;
    this->sample_rate = sample_rate;
    this->callJava = callJava;

    this->isCat = false;
    this->end_time  =0;
    this->showPcm = false;

    queue = new XYQueue(playStatus);
    buffer = static_cast<uint8_t *>(av_malloc(sample_rate * 2 * 2));

    sampleBuffer = (SAMPLETYPE *) (malloc(sample_rate * 2 * 2));
    soundTouch = new SoundTouch();

    soundTouch->setSampleRate(sample_rate);
    soundTouch->setChannels(mute);
    soundTouch->setPitch(pitch);
    soundTouch->setTempo(speed);

}

XYAudio::~XYAudio() {

}

void *decodPlay(void *data) {
    XYAudio *xyAudio = static_cast<XYAudio *>(data);
    //初始化OpenSLES
    xyAudio->initOpenSLES();
    pthread_exit(&xyAudio->thread_play);
}

//拆分
void *pcmCallBack(void *data){
    XYAudio *audio = static_cast<XYAudio *>(data);

   // audio->bufferQueue = new XYBufferQueue(audio->playStatus);


    pthread_exit(&audio->pcmCallBackThread);

}
void XYAudio::play() {

    pthread_create(&thread_play, NULL, decodPlay, this);

    //可以通过一个变量判断是否开启拆分PCM的线程
    //pthread_create(&pcmCallBackThread,NULL,,this);
}

//FILE *outFile = fopen("/sdcard/music_write.pcm","w");

int XYAudio::resampleAudio(void **pcmbuff) {
    data_size = 0;

    while (playStatus != NULL && !playStatus->exit) {

        if (playStatus->seek) {
            av_usleep(1000 * 100);
            continue;
        }
        //根据队列状态 显示加载状态
//        LOGD("queue->getQueueSize() = %d",queue->getQueueSize())
        if (queue->getQueueSize() == 0) {//加载中
            if (!playStatus->load) {
                playStatus->load = true;
                callJava->onCallLoad(CHILD_THREAD, true);//子线程
            }
            av_usleep(1000 * 100);
            continue;
        } else {
            if (playStatus->load) {
                playStatus->load = false;
                callJava->onCallLoad(CHILD_THREAD, false);
            }
        }

        //只有当avframe读取完成之后 才读取下一个avpacket 里面的avframe
        if (readFrameFinshed) {
            avPacket = av_packet_alloc();
            if (queue->getAvpacket(avPacket) != 0) {
                av_packet_free(&avPacket);
                av_free(avPacket);
                avPacket = NULL;
                continue;
            }
            //解码avPacket

            ret = avcodec_send_packet(avCodecContext, avPacket);//放入解码
            if (ret != 0) {
                av_packet_free(&avPacket);
                av_free(avPacket);
                avPacket = NULL;
                continue;
            }

        }


        avFrame = av_frame_alloc();
        ret = avcodec_receive_frame(avCodecContext, avFrame);
        if (ret == 0) {
            readFrameFinshed = false;

            //重采样
            if (avFrame->channels > 0 && avFrame->channel_layout == 0) {
                //根据声道数返回声道布局
                avFrame->channel_layout = av_get_default_channel_layout(avFrame->channels);

            } else if (avFrame->channels > 0 && avFrame->channel_layout > 0) {
                avFrame->channels = av_get_channel_layout_nb_channels(avFrame->channel_layout);
            }

            //设置采样参数
            SwrContext *swr_ctx = NULL;
            swr_ctx = swr_alloc_set_opts(NULL,
                                         AV_CH_LAYOUT_STEREO,
                                         AV_SAMPLE_FMT_S16,
                                         avFrame->sample_rate,
                                         avFrame->channel_layout,
                                         static_cast<AVSampleFormat>(avFrame->format),
                                         avFrame->sample_rate,
                                         NULL,
                                         NULL);

            if (!swr_ctx || swr_init(swr_ctx) < 0) {
                av_packet_free(&avPacket);
                av_free(avPacket);
                avPacket = NULL;
                av_frame_free(&avFrame);
                av_free(avFrame);
                avFrame = NULL;

                if (swr_ctx != NULL) {
                    swr_free(&swr_ctx);
                    swr_ctx = NULL;
                }
                continue;

            }

            //开始重采样

            //获取采样个数
            nb = swr_convert(
                    swr_ctx,
                    &buffer,
                    avFrame->nb_samples,
                    (const uint8_t **) (avFrame->data),
                    avFrame->nb_samples);

            //声道布局  声道个数
            int out_channels = av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO);
//            LOGD("采样个数 %d 声道布局个数 %d av_get_bytes_per_sample(AV_SAMPLE_FMT_S16) = %d ",nb,out_channels,av_get_bytes_per_sample(AV_SAMPLE_FMT_S16))
            data_size = nb * out_channels * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16);
            //写入存储卡
//            fwrite(buffer, 1, data_size, outFile);
//            LOGD("data_size is %d", data_size);

//            LOGD("重采样 获取data %d",data_size);
            now_time = avFrame->pts * av_q2d(time_base);
            if (now_time < clock) {
                now_time = clock;
            }
            clock = now_time;

            //入参出参pcmbuff 赋值
            *pcmbuff = buffer;

            av_packet_free(&avPacket);
            av_free(avPacket);
            avPacket = NULL;
            av_frame_free(&avFrame);
            av_free(avFrame);
            avFrame = NULL;
            swr_free(&swr_ctx);
            readFrameFinshed = true;
            break;
        } else {
            //读取完了
            readFrameFinshed = true;
            av_packet_free(&avPacket);
            av_free(avPacket);
            avPacket = NULL;
            av_frame_free(&avFrame);
            av_free(avFrame);
            avFrame = NULL;
            continue;
        }
    }
//    LOGD("是否一直调用 data_size = %d", data_size)
    return data_size;
}

int XYAudio::getSoundTouchData() {

    while (playStatus != NULL && !playStatus->exit) {
        out_buffer = NULL;
        if (finished) {
            finished = false;
            data_size = resampleAudio(reinterpret_cast<void **>(&out_buffer));
            if (data_size > 0) {
                //8位转 16位 公式
                for (int i = 0; i < data_size / 2 + 1; ++i) {
                    sampleBuffer[i] = (out_buffer[i * 2] | ((out_buffer[i * 2 + 1])) << 8);
                }
                soundTouch->putSamples(sampleBuffer, nb);
                //第一个为接收数据的参数，第二个最大可以接收多少sample
                num = soundTouch->receiveSamples(sampleBuffer, data_size / 4);
            } else {
                soundTouch->flush();
            }
        }
        if (num == 0) {
            finished = true;
            continue;
        } else {
            if (out_buffer == NULL) {
                num = soundTouch->receiveSamples(sampleBuffer, data_size / 4);
                if (num == 0) {
                    finished = true;
                    continue;
                }
            }
            return num;
        }
    }
    return 0;
}


void pcmBufferCallBack(SLAndroidSimpleBufferQueueItf bf, void *context) {

    //获取数据 重采样
    XYAudio *xyAudio = (XYAudio *) (context);
    if (xyAudio != NULL) {
        int samplesize = xyAudio->getSoundTouchData();//获取通过SoundTouch的 采样的个数
//        int buffersize = xyAudio->resampleAudio();
//        LOGD("回调 buffersize = %d", buffersize);
        if (samplesize >= 0) {

            //获取当前实际的clock 根据pcm的大小求播放时间 为了时间更精确
//            LOGD("buffersize = %d \n xyAudio->sample_rate * 2 * 2 = %d", buffersize,xyAudio->sample_rate * 2 * 2);
            xyAudio->clock += samplesize / ((double) (xyAudio->sample_rate * 2 * 2));

            //如果大于0.1秒
            if (xyAudio->clock - xyAudio->last_time >= 0.1) {
                xyAudio->last_time = xyAudio->clock;
                //进行回调播放时间
//                LOGD("pcmBufferCallBack 当前时间 = %d , pcmBufferCallBack 总 = %d", xyAudio->clock,
//                     xyAudio->duration);
                xyAudio->callJava->onCallTimeInfo(CHILD_THREAD, xyAudio->clock, xyAudio->duration);
            }
            if (xyAudio->isRecordPcm) {
                //回调pcm转化成aac  onCallPcmToAAc
                xyAudio->callJava->onCallPcmToAAC(CHILD_THREAD, samplesize * 2 * 2,
                                                  xyAudio->sampleBuffer);

            }

            //回调音量
            xyAudio->callJava->onCallVolumeDaB(CHILD_THREAD,
                                               xyAudio->getPCMDB((char *) (xyAudio->sampleBuffer),
                                                                 samplesize * 4));

            (*xyAudio->pcmBufferQueue)->Enqueue(xyAudio->pcmBufferQueue,
                                                (char *) xyAudio->sampleBuffer,
                                                samplesize * 2 * 2);
//            (*xyAudio->pcmBufferQueue)->Enqueue(xyAudio->pcmBufferQueue,
//                                                (char *) xyAudio->buffer,
//                                                buffersize);
            if (xyAudio->isCat) {
               //裁剪
                if (xyAudio->showPcm) {
                    xyAudio->callJava->onCallPcmInfo(xyAudio->sampleBuffer,samplesize * 2 * 2);
                }
//                LOGD("clock = %d, end_time = %d",xyAudio->clock,xyAudio->end_time);
                if (xyAudio->clock > xyAudio->end_time) {
                    LOGD("裁剪退出");
                    xyAudio->playStatus->exit = true;
                }
            }
        }
    }

}

void XYAudio::initOpenSLES() {

    SLresult result;
    //第一步------------------------------------------
    // 创建引擎对象
    slCreateEngine(&engineObject, 0, 0, 0, 0, 0);
    (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
    (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);


    //第二步-------------------------------------------
    // 创建混音器
    const SLInterfaceID mids[1] = {SL_IID_ENVIRONMENTALREVERB};
    const SLboolean mreq[1] = {SL_BOOLEAN_FALSE};
    result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, mids, mreq);
    (void) result;
    result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
    (void) result;
    result = (*outputMixObject)->GetInterface(outputMixObject, SL_IID_ENVIRONMENTALREVERB,
                                              &outputMixEnvironmentalReverb);
    if (SL_RESULT_SUCCESS == result) {
        result = (*outputMixEnvironmentalReverb)->SetEnvironmentalReverbProperties(
                outputMixEnvironmentalReverb, &reverbSettings);
        (void) result;
    }
    SLDataLocator_OutputMix outputMix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};

    // 第三步--------------------------------------------
    // 创建播放器
    SLDataLocator_AndroidSimpleBufferQueue android_queue = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
                                                            2};
    SLDataFormat_PCM pcm = {
            SL_DATAFORMAT_PCM,//播放pcm格式的数据
            2,//2个声道（立体声）
            static_cast<SLuint32>(getCurrentSampleRateForOpensles(sample_rate)),//44100hz的频率
            SL_PCMSAMPLEFORMAT_FIXED_16,//位数 16位
            SL_PCMSAMPLEFORMAT_FIXED_16,//和位数一致就行
            SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,//立体声（前左前右）
            SL_BYTEORDER_LITTLEENDIAN//结束标志
    };

    SLDataSource slDataSource = {&android_queue, &pcm};
    SLDataSink audioSnk = {&outputMix, NULL};

    const SLInterfaceID ids[4] = {SL_IID_BUFFERQUEUE, SL_IID_MUTESOLO, SL_IID_PLAYBACKRATE,
                                  SL_IID_VOLUME};//bufferqueue 功能  volume 音量功能 SL_IID_MUTESOLO 声道功能
    const SLboolean req[4] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};

    result = (*engineEngine)->CreateAudioPlayer(engineEngine, &pcmPlayerObject, &slDataSource,
                                                &audioSnk, 3, ids, req);
    // 初始化播放器
    (*pcmPlayerObject)->Realize(pcmPlayerObject, SL_BOOLEAN_FALSE);

    //得到接口后调用  获取Player接口
    (*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_PLAY, &pcmPlayerPlay);

    //第四步---------------------------------------
    // 创建缓冲区和回调函数
    (*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_BUFFERQUEUE, &pcmBufferQueue);

    //缓冲接口回调
    (*pcmBufferQueue)->RegisterCallback(pcmBufferQueue, pcmBufferCallBack, this);
    //获取音量接口
    int ref = (*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_VOLUME, &pcmPlayerVolume);
    LOGD("获取音量接口 ref = %d", ref);
    //获取声道接口
    ref = (*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_MUTESOLO, &pcmPlayerMute);
    LOGD("获取声道接口 ref = %d", ref);

    //刚开始设置音量
    setVolmue(volumePercent);
    //设置声道
    setMute(mute);

    //第五步----------------------------------------
    // 设置播放状态
    (*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay, SL_PLAYSTATE_PLAYING);


    //第六步----------------------------------------
    // 主动调用回调函数开始工作
    LOGD("主动调用回调函数开始工作");
    pcmBufferCallBack(pcmBufferQueue, this);
}

int XYAudio::getCurrentSampleRateForOpensles(int sample_rate) {
    int rate = 0;
    switch (sample_rate) {
        case 8000:
            rate = SL_SAMPLINGRATE_8;
            break;
        case 11025:
            rate = SL_SAMPLINGRATE_11_025;
            break;
        case 12000:
            rate = SL_SAMPLINGRATE_12;
            break;
        case 16000:
            rate = SL_SAMPLINGRATE_16;
            break;
        case 22050:
            rate = SL_SAMPLINGRATE_22_05;
            break;
        case 24000:
            rate = SL_SAMPLINGRATE_24;
            break;
        case 32000:
            rate = SL_SAMPLINGRATE_32;
            break;
        case 44100:
            rate = SL_SAMPLINGRATE_44_1;
            break;
        case 48000:
            rate = SL_SAMPLINGRATE_48;
            break;
        case 64000:
            rate = SL_SAMPLINGRATE_64;
            break;
        case 88200:
            rate = SL_SAMPLINGRATE_88_2;
            break;
        case 96000:
            rate = SL_SAMPLINGRATE_96;
            break;
        case 192000:
            rate = SL_SAMPLINGRATE_192;
            break;
        default:
            rate = SL_SAMPLINGRATE_44_1;
    }
    return rate;
}

void XYAudio::pause() {
    if (pcmPlayerObject != NULL) {
        (*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay, SL_PLAYSTATE_PAUSED);
    }
}

void XYAudio::resume() {

    if (pcmPlayerObject != NULL) {
        (*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay, SL_PLAYSTATE_PLAYING);
    }

}

void XYAudio::stop() {
    if (pcmPlayerObject != NULL) {
        (*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay, SL_PLAYSTATE_STOPPED);
    }
}


void XYAudio::release() {

    stop();
    if (queue != NULL) {
        delete (queue);
        queue = NULL;
    }

    //回收OpenSLES  播放器
    if (pcmPlayerObject != NULL) {
        (*pcmPlayerObject)->Destroy(pcmPlayerObject);
        pcmPlayerObject = NULL;
        pcmPlayerPlay = NULL;
        pcmBufferQueue = NULL;
        pcmPlayerMute = NULL;
        pcmPlayerVolume = NULL;

    }

    //回收混音器
    if (outputMixObject != NULL) {
        (*outputMixObject)->Destroy(outputMixObject);
        outputMixObject = NULL;
        outputMixEnvironmentalReverb = NULL;
    }

    //回收引擎
    if (engineObject != NULL) {
        (*engineObject)->Destroy(engineObject);
        engineObject = NULL;
        engineEngine = NULL;
    }

    if (buffer != NULL) {
        free(buffer);
        buffer = NULL;
    }

    if (out_buffer != NULL) {
        out_buffer = NULL;
    }

    if (soundTouch != NULL) {
        delete soundTouch;
        soundTouch = NULL;
    }

    if (sampleBuffer != NULL) {
        free(sampleBuffer);
        sampleBuffer = NULL;
    }

    if (avCodecContext != NULL) {
        avcodec_close(avCodecContext);
        avcodec_free_context(&avCodecContext);
        avCodecContext = NULL;
    }

    if (playStatus != NULL) {
        playStatus = NULL;
    }

    if (callJava != NULL) {
        callJava = NULL;
    }


}

void XYAudio::setVolmue(int percent) {
    volumePercent = percent;
    if (pcmPlayerVolume != NULL) {

        if (percent > 30) {
            (*pcmPlayerVolume)->SetVolumeLevel(pcmPlayerVolume, (100 - percent) * -20);
        } else if (percent > 25) {
            (*pcmPlayerVolume)->SetVolumeLevel(pcmPlayerVolume, (100 - percent) * -22);
        } else if (percent > 20) {
            (*pcmPlayerVolume)->SetVolumeLevel(pcmPlayerVolume, (100 - percent) * -25);
        } else if (percent > 15) {
            (*pcmPlayerVolume)->SetVolumeLevel(pcmPlayerVolume, (100 - percent) * -28);
        } else if (percent > 10) {
            (*pcmPlayerVolume)->SetVolumeLevel(pcmPlayerVolume, (100 - percent) * -30);
        } else if (percent > 5) {
            (*pcmPlayerVolume)->SetVolumeLevel(pcmPlayerVolume, (100 - percent) * -34);
        } else if (percent > 3) {
            (*pcmPlayerVolume)->SetVolumeLevel(pcmPlayerVolume, (100 - percent) * -37);
        } else if (percent > 0) {
            (*pcmPlayerVolume)->SetVolumeLevel(pcmPlayerVolume, (100 - percent) * -40);
        } else {
            (*pcmPlayerVolume)->SetVolumeLevel(pcmPlayerVolume, (100 - percent) * -100);
        }
    }
}

void XYAudio::setMute(int mute) {
    LOGD("声道 : %d", mute);
    this->mute = mute;
    if (pcmPlayerMute != NULL) {
        if (mute == 0) {//right 右声道 把1 关闭
            (*pcmPlayerMute)->SetChannelMute(pcmPlayerMute, 1, false);
            (*pcmPlayerMute)->SetChannelMute(pcmPlayerMute, 0, true);
        } else if (mute == 1) {//left
            (*pcmPlayerMute)->SetChannelMute(pcmPlayerMute, 0, false);
            (*pcmPlayerMute)->SetChannelMute(pcmPlayerMute, 1, true);

        } else if (mute == 2) {//center 都关闭
            (*pcmPlayerMute)->SetChannelMute(pcmPlayerMute, 1, false);
            (*pcmPlayerMute)->SetChannelMute(pcmPlayerMute, 0, false);
        }
    }

}

void XYAudio::setPitch(float pitch) {
    this->pitch = pitch;
    if (soundTouch != NULL) {
        soundTouch->setPitch(pitch);
    }
}

void XYAudio::setSpeed(float speed) {
    this->speed = speed;
    if (soundTouch != NULL) {
        soundTouch->setTempo(speed);
    }
}

int XYAudio::getPCMDB(char *pcmcata, size_t pcmsize) {
    int db = 0;
    short int pervalue = 0;//每一位的16位值
    double sum = 0;

    for (int i = 0; i < pcmsize; i += 2) {
        //使用拷贝函数 每次取往后移动指针
        memcpy(&pervalue, pcmcata + 1, 2);//每次取两个值
        sum += abs(pervalue);//求绝对值的和

    }
    sum = sum / (pcmsize / 2);
    if (sum > 0) {
        db = (int) 20.0 * log10(sum);
    }
    return db;
}

/**
 * 控制开始录制停止的函数
 */
void XYAudio::startStopRecord(bool start) {
    this->isRecordPcm = start;

}













